using Cysharp.Threading.Tasks;
using GameFramework;
using GameFramework.Resource;
using System.Collections.Generic;
using UnityEngine;
using UnityGameFramework.Runtime;

namespace DTFramework.Scriptable
{
    [DisallowMultipleComponent]
    [AddComponentMenu("DTFramework/Scriptable")]
    public class ScriptableComponent : GameFrameworkComponent
    {
        private Dictionary<string, UniTaskCompletionSource<ScriptableObject>> m_TaskScriptableObjectDict;

        [SerializeField]
        private List<ScriptableObject> m_ScriptableObjectList;
        /// <summary>
        /// 游戏框架组件初始化。
        /// </summary>
        protected override void Awake()
        {
            base.Awake();
            m_TaskScriptableObjectDict = new Dictionary<string, UniTaskCompletionSource<ScriptableObject>>();
            m_ScriptableObjectList = new List<ScriptableObject>();
        }

        /// <summary>
        /// 加载 所有 ScriptableObject 资源
        /// </summary>
        /// <param name="scriptableNames"></param>
        /// <returns></returns>
        public async UniTask LoadAllAsync(List<string> scriptableNames)
        {
            if (scriptableNames == null)
            {
                Debug.LogError("==> 加载 scriptableNames 为空！");
                return;
            }
            foreach (var scriptableName in scriptableNames)
            {
                await LoadAsync(scriptableName);
            }
        }
        /// <summary>
        /// / 加载 ScriptableObject 资源
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="scriptableName"></param>
        /// <returns></returns>
        public async UniTask<T> LoadAsync<T>(string scriptableName) where T : ScriptableObject
        {
            var result = await LoadAssetAsync(scriptableName);
            return result as T;
        }
        public UniTask<ScriptableObject> LoadAsync(string scriptableName)
        {
            var assetPath = AssetPath.GetScriptableAsset(scriptableName);
            return LoadAssetAsync(assetPath);
        }
        /// <summary>
        ///  查找 ScriptableObject
        /// </summary>
        public T Find<T>() where T : ScriptableObject
        {
            if (m_ScriptableObjectList == null) return null;
            foreach (var item in m_ScriptableObjectList)
            {
                if (item is T) return (T)item;
            }
            return null;
        }
        /// <summary>
        /// 移除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void Remove<T>() where T : ScriptableObject
        {
            if (m_ScriptableObjectList == null) return;

            T data = null;
            foreach (var item in m_ScriptableObjectList)
            {
                if (item is T) data = (T)item;
            }
            m_ScriptableObjectList.Remove(data);
            GameEntry.Resource.UnloadAsset(data);
        }
        /// <summary>
        /// 移除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void RemoveAll()
        {
            if (m_ScriptableObjectList == null) return;

            foreach (var item in m_ScriptableObjectList)
            {
                GameEntry.Resource.UnloadAsset(item);
            }
            m_ScriptableObjectList.Clear();
        }

        private UniTask<ScriptableObject> LoadAssetAsync(string assetPath)
        {
            GameEntry.Resource.LoadAsset(assetPath,
                typeof(ScriptableObject),
                new LoadAssetCallbacks(OnLoadScriptableObjectAssetSuccess, OnLoadScriptableObjectAssetFail));

            var tcs = new UniTaskCompletionSource<ScriptableObject>();
            m_TaskScriptableObjectDict.Add(assetPath, tcs);
            return tcs.Task;
        }
        private void OnLoadScriptableObjectAssetSuccess(string assetName, object asset, float duration, object userData)
        {
            var scriptable = asset as ScriptableObject;
            if (m_TaskScriptableObjectDict.TryGetValue(assetName, out UniTaskCompletionSource<ScriptableObject> tcs))
            {
                tcs.TrySetResult(scriptable);
                if (m_ScriptableObjectList == null)
                {
                    m_ScriptableObjectList = new List<ScriptableObject> { };
                }
                if (!m_ScriptableObjectList.Contains(scriptable))
                {
                    m_ScriptableObjectList.Add(scriptable);
                }
                m_TaskScriptableObjectDict.Remove(assetName);
            }
        }
        private void OnLoadScriptableObjectAssetFail(string assetName, LoadResourceStatus status, string errorMessage, object userData)
        {
            if (m_TaskScriptableObjectDict.TryGetValue(assetName, out UniTaskCompletionSource<ScriptableObject> tcs))
            {
                Log.Error($"初始化加载 ScriptableObject 失败：{assetName}，status：{status}，errorMessage：{errorMessage}");
                tcs.TrySetException(new GameFrameworkException(errorMessage));
                m_TaskScriptableObjectDict.Remove(assetName);
            }
        }
    }
}
